import {Injectable} from "@angular/core";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {NavController, Tabs, App, Events, Platform} from "ionic-angular";
import {FingerprintAIO, FingerprintOptions} from "@ionic-native/fingerprint-aio";
import {DeviceService} from "../device/device.service";
import {TOKENURL, SIGURL} from "../../../config/url.config";
import {FileService} from "../file/file.service";
import {CryptoService} from "../../common/crypto.service";
import {ToastService} from "../../common/toast.service";
import {
    ConfigurationService,
    PersonalSecurity,
    UnlockGraph,
    Profile,
    SystemSecurity,
    User
} from "../../common/configuration.service";

@Injectable()
export class UnlockService {

    private system: SystemSecurity;
    private personal: PersonalSecurity;
    private profile: Profile;
    private navCtrl: NavController
    private maxUnlockAttempTimes: number = 5;                   // 密码/手势登录最大尝试次数
    private passwordExpiry: number = 10 * 3600 * 1000           // 用户名密码登录后Token失效时间（毫秒）：10 小时
    private assistAuthExpiry: number = 7 * 24 * 3600 * 1000;

    private resumeTimeout: number = 15 * 60 * 1000;
    // 使用图形或指纹登录的Token失效时间（毫秒）：7 天
    // private lockoutEffectivePeriod: number;                  // 开启手势指纹后，锁定APP时间（分钟）
    private validateTip: number;
    private rootNav: NavController;

    constructor(private app: App,
                private fingerprint: FingerprintAIO,
                private http: HttpClient,
                private events: Events,
                private device: DeviceService,
                private crypto: CryptoService,
                private configService: ConfigurationService,
                private toastService: ToastService,
                private platform: Platform,
                private fileService: FileService) {

        this.system = this.configService.system;
        this.personal = this.configService.personal;
        this.profile = this.configService.profile;

        app.viewDidLoad.subscribe(() => {
            // console.log(app.getActiveNavs())
            this.rootNav = app.getRootNavs()[0];
        })
    }

    public set nav(value: NavController) {
        this.navCtrl = value;
    }

    // public get maxUnlockAttemps(): number {
    //     return this.maxUnlockTry;
    // }

    public isLockedOut(): Promise<boolean> {
        return Promise.resolve(this.system.isLockedOut.value !== false.toString());
    }

    public isGraphicEnabled(): Promise<boolean> {
        return Promise.resolve(this.personal.isGraphicEnabled.value === true.toString());
    }

    /**
     * 用户是否设置了指纹登录
     * @returns {Promise<boolean>}
     */
    public isFingerprintEnabled(): Promise<boolean> {
        return this.isFingerprintAvailable().then(available => {
            return available && this.personal.isFingerprintEnabled.value === true.toString();
        }).catch(error => {
            return false;
        });
    }

    /**
     * 机器是否开启了指纹
     * @returns {Promise<boolean>}
     */
    public isFingerprintAvailable(): Promise<boolean> {
        return this.fingerprint.isAvailable().then((res) => {
            console.info(res);
            return true
        }).catch((error) => {
            console.log("isFingerprintAvailable:", error);
            if (this.platform.is('ios')) {
                try {
                    let errObj = JSON.parse(error),
                        errCode = errObj.code;
                    if (errCode == '-7') {
                        //表示无指纹
                        return false;
                    }

                    if (errCode == '-8') {
                        //表示被锁定 锁定一定会有指纹
                        return true;

                    }
                } catch (err) {
                    return false
                }
            }
            return false
        });
    }

    public isFingerprintEnrolled(): Promise<boolean> {
        return Promise.resolve(true);
    }

    public hasStoredGraphic(): Promise<boolean> {
        const graph = this.personal.unlockGraph.value;
        return Promise.resolve(this.isStringEmpty(graph));
    }

    public passwordErrorTimes(): Promise<number> {
        const times = parseInt(this.personal.passwordErrorTimes.value);
        return Promise.resolve(times);
    }

    public passwordRemainAttemps(): Promise<number> {
        let tried = parseInt(this.personal.passwordErrorTimes.value);
        if (isNaN(tried)) {
            tried = 0;
        }
        return Promise.resolve(this.maxUnlockAttempTimes - tried);
    }

    public graphicErrorTimes(): Promise<number> {
        const times = parseInt(this.personal.graphicErrorTimes.value);
        return Promise.resolve(times);
    }

    // public lastAccessed(): Promise<number> {
    //     const lastAccessed = parseInt(this.security.lastAccessed.value);
    //     return Promise.resolve(lastAccessed);
    // }

    //手势设置时  验证
    public switchGraphicLogin(enabled: boolean) {

        this.personal.isGraphicEnabled.value = enabled.toString();
        if (enabled) {
            this.personal.isFingerprintEnabled.value = false.toString();
        }
        this.refresh();
    }

    //指纹设置时 验证
    public switchFingerprintLogin(enabled: boolean) {
        this.markAccess();//立即更新登录时间
        this.personal.isFingerprintEnabled.value = enabled.toString();
        if (enabled) {
            this.personal.isGraphicEnabled.value = false.toString();
        }
        this.refresh();
    }


    public validatePassword(password: string, name?: string): Promise<String> {
        if (typeof name === "undefined") {
            name = this.profile.userName.value;
        }
        return this.login(name, password, false).then(() => {
            //登录成功后，密码验证错误次数重置为0
            this.personal.passwordErrorTimes.value = "0";
            return 'success';
        }).catch(error => {
            if (navigator.onLine) {
                //如果有网并且是用户名密码错误
                if (this.validateTip === ErrorTipCode.VALIDATE_FAIL) {
                    const storedValue = this.personal.passwordErrorTimes.value;
                    const errorTimes = (storedValue ? parseInt(storedValue) : 0) + 1;
                    if (errorTimes >= this.maxUnlockAttempTimes) {
                        this.system.isLockedOut.value = true.toString();
                        this.system.currentUser.value = "";
                        this.personal.passwordErrorTimes.value = this.maxUnlockAttempTimes.toString();
                        this.removeToken();
                        this.goToBasicLoginPage();
                    }
                    else {
                        this.personal.passwordErrorTimes.value = errorTimes.toString();
                    }
                    return 'fail'
                } else {
                    return 'netWork';
                }
            } else {
                //断网
                return 'offLine';
            }
        });
    }

    public validateFingerprint(): Promise<boolean> {
        const proc = async () => {
            if (await this.isFingerprintAvailable()) {
                let options: FingerprintOptions = {
                    clientId: '!ADP@Fingerprint',
                    clientSecret: 'p@ssm0rp', //Only necessary for Android
                    disableBackup: true,
                    localizedFallbackTitle: 'Use Pin', //Only for iOS
                    localizedReason: '请验证指纹' //Only for iOS
                };
                return this.fingerprint.show(options).then((result: any) => {
                    return true;
                }).catch((error: any) => {
                    return false
                });
            }
            else {
                //指纹不可用 既删除了指纹
                return Promise.resolve(false);
            }
        };

        return proc();
    }

    public validateGraphic(value: string): Promise<boolean> {
        const graphConfig: UnlockGraph = this.personal.unlockGraph as UnlockGraph;
        const graph = graphConfig.value;

        if (this.isStringEmpty(graph)) {
            return Promise.resolve(false);
        }

        const isValid = graphConfig.equal(value);
        if (!isValid) {
            if (this.personal.graphicErrorTimes.value === 'NaN') {
                this.personal.graphicErrorTimes.value = '0';
            }
            const times = parseInt(this.personal.graphicErrorTimes.value);
            this.personal.graphicErrorTimes.value = (times + 1).toString();
        }
        return Promise.resolve(isValid);
    }

    //存储手势验证密码
    public storeGraphic(value: string) {
        this.markAccess();
        this.personal.unlockGraph.value = value;
    }

    /**
     * 用户操作，记录最后访问时间。用于判断用户有多长时间没有操作APP了
     *
     * @memberof UnlockService
     */
    public markAccess() {
        const now: number = Date.now();
        this.personal.lastAccessed.value = now.toString();
    }

    /**
     * 判断用户Token是否已过期
     *
     * @returns {Promise<boolean>}
     * @memberof UnlockService
     */
    public isTokenExpired(): Promise<boolean> {
        // 无论是用户名密码登录，还是指纹手势登录，如果判断token过期，都转到用户名密码登录界面，重新验证用户
        const storedValue: string = this.personal.lastAccessed.value;
        // 登录后必须生成值，如果值为空认为是异常状态，需要回到用户名密码登录页面
        if (this.isStringEmpty(storedValue)) {
            return Promise.resolve(true);
        }

        const proc = async () => {
            const lastAccessed: number = parseInt(storedValue);
            const now: number = Date.now();

            let isExpired: boolean;
            if (await this.isFingerprintEnabled() || await this.isGraphicEnabled()) {
                isExpired = (now - lastAccessed > this.assistAuthExpiry);
            }
            else {
                isExpired = (now - lastAccessed > this.passwordExpiry);
            }

            if (isExpired) {
                this.system.isLockedOut.value = true.toString();
                this.system.currentUser.value = "";
                this.removeToken();
            }
            return Promise.resolve(isExpired);
        };

        return proc();
    }

    public removeToken() {
        this.personal.accessToken.remove();
        this.personal.refreshToken.remove();
    }

    public async getLoginPage(): Promise<string> {
        const accessToken = this.personal.accessToken.value;
        const refreshToken = this.personal.refreshToken.value;
        if (this.isStringEmpty(accessToken) || this.isStringEmpty(refreshToken)) {
            return PageName.basicLogin;
        }

        if (await this.isFingerprintEnabled()) {
            return PageName.fingerprintLogin;
        }
        if (await this.isGraphicEnabled()) {
            if (parseInt(this.personal.graphicErrorTimes.value) >= 5) {
                return PageName.basicLogin;//手势输错五次 跳转密码登录界面
            } else {
                return PageName.graphicLogin;
            }
        }

        return PageName.basicLogin;
    }

    public goToBasicLoginPage() {
        this.rootNav.setRoot(PageName.basicLogin);
    }

    public goToGraphicLoginPage() {
        this.rootNav.setRoot(PageName.graphicLogin);
    }

    public goToFingerprintLoginPage() {
        this.rootNav.setRoot(PageName.fingerprintLogin);
    }

    public goToContentPage() {
        this.rootNav.setRoot(PageName.contentTab);
    }

    public async getDisplayPage(): Promise<string> {
        if (await this.isLockedOut()) {
            return await this.getLoginPage();
        }
        else {
            // token过期，必须输入用户名密码验证
            if (await this.isTokenExpired() || !await this.verifyToken()) {
                return PageName.basicLogin;
            }
            else {
                if (this.personal.isFingerprintEnabled.value === true.toString()) {
                    //如果设置了指纹解锁
                    if (await this.isFingerprintAvailable()) {
                        //指纹可用 指纹界面
                        return PageName.fingerprintLogin;
                    } else {
                        //但是呢 又删除了指纹
                        this.personal.isFingerprintEnabled.value = 'false';
                        return PageName.basicLogin;
                    }
                }

                if (await this.isGraphicEnabled()) {
                    if (parseInt(this.personal.graphicErrorTimes.value) >= 5) {
                        return PageName.basicLogin;//手势输错五次 跳转密码登录界面
                    } else {
                        return PageName.graphicLogin;
                    }
                }
                return PageName.contentTab;
            }
        }
    }

    public verifyCaptchaSignature(token: string, scene: string, session: string, signature: string): Promise<any> {
        const url = SIGURL + `?token=${token}&scene=${scene}&sig=${signature}&sessionId=${session}`;
        const uuid = this.device.uuid();
        const headers = new HttpHeaders({
            "device": uuid
        });
        return this.http.get(url, {headers: headers}).toPromise();
    }

    /**
     * 登录界面 与 手势、指纹密码验证使用
     * @param {string} password
     * @param {string} name
     * @param {boolean} isShowTip 是否展示提示
     * @returns {Promise<String>}
     */
    public login(name: string, password: string, isShowTip: boolean = true): Promise<any> {
        const body = {
            "grant_type": "password",
            "username": name,
            "password": password,
        };

        return this.requestToken(body, isShowTip).then(response => {
            // 先设置系统配置，再设置个人配置项
            this.system.isLockedOut.value = response == null ? true.toString() : false.toString();
            this.registerCurrentUser(name);
            this.personal = this.configService.personal;
            this.personal.graphicErrorTimes.value = "0";
            this.personal.lastAccessed.value = Date.now().toString();
            this.updateConfiguration(response);
            //首页全量角色权限
            localStorage.setItem('fullRoles', this.getFullRoles(response.roles));
        });
    }

    //处理全量角色
    private getFullRoles(roles: Array<UserRole>): string {
        let arr = [];
        for (let role of roles) {
            arr.push(role.authority);
        }

        return arr.join(',');
    }

    public async refresh(): Promise<boolean> {
        const refreshToken = this.personal.refreshToken.value;
        if (this.isStringEmpty(refreshToken)) {
            return Promise.resolve(false);
        }

        const body: { [prop: string]: string } = {
            "grant_type": "refresh_token",
            "refresh_token": refreshToken,
        };
        if (await this.isFingerprintEnabled() || await this.isGraphicEnabled()) {
            body.other_login = "1";
        }
        return this.requestToken(body).then(response => {
            this.updateConfiguration(response);
            return true;
        }, error => {
            this.events.publish("http:error", error);
            return false;
        });
    }

    /**
     * 点击‘退出’时，锁定/注销用户
     *
     * @memberof UnlockService
     */
    public lockout(clearToken?: boolean) {

        // 特殊处理：停止index页面的定时器，防止定时器一直运行导致错误
        const activeChildNavs = this.rootNav.getActiveChildNavs();
        if (activeChildNavs.length > 0) {
            let tabs = activeChildNavs[0] as Tabs;

            if (tabs.getSelected()) {
                //tabs.select(tabs.length() - 1);
                const handler = tabs.getSelected().getActive().instance.leaveHandler;
                if (typeof handler !== "undefined") {
                    handler();
                }
            }
        }

        this.system.isLockedOut.value = true.toString();
        this.system.currentUser.value = "";
        if (clearToken === true) {
            this.removeToken();
        }

        this.getLoginPage().then(page => {
            this.rootNav.setRoot(page);
        })
    }

    private registerCurrentUser(name: string): User {
        const storedDeviceUsers = this.system.deviceUsers.value;

        if (typeof storedDeviceUsers === "undefined" || storedDeviceUsers === null) {
            const newUser: User = {id: this.crypto.md5.digest(Math.random().toString()), name: name};
            this.system.currentUser.value = JSON.stringify(newUser);

            this.system.deviceUsers.value = JSON.stringify([newUser]);

            return newUser;
        }
        else {
            const deviceUsers = JSON.parse(storedDeviceUsers) as User[];
            const index = deviceUsers.findIndex(user => user.name === name);
            if (index === -1) {
                const newUser: User = {id: this.crypto.md5.digest(Math.random().toString()), name: name};
                this.system.currentUser.value = JSON.stringify(newUser);

                deviceUsers.push(newUser);
                this.system.deviceUsers.value = JSON.stringify(deviceUsers);

                return newUser;
            }
            else {
                const user = deviceUsers[index];
                this.system.currentUser.value = JSON.stringify(user);

                return user;
            }
        }

    }

    private isStringEmpty(value: string): boolean {
        return (typeof value === "undefined" || value === null || value.length === 0);
    }

    private requestToken(body: { [prop: string]: string }, isShowTip: boolean = true): Promise<TokenResponse> {
        let requestId = localStorage.getItem("requestId");
        body.requestId = requestId;
        body.client_secret = "123456";
        body.client_id = "msl";
        body.appkey = "app";
        body.secretkey = "111";

        const url = TOKENURL;
        const headers = new HttpHeaders({
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "device": this.device.uuid()
        });
        return this.http.post(url, this.getEncodedBody(body), {headers: headers}).toPromise().then(res => {
            localStorage.setItem("requestId", "test");
            this.personal.passwordErrorTimes.value = "0";//登录成功，密码错误次数重置
            return res as TokenResponse;
        }, (err) => {
            localStorage.setItem("requestId", "test");
            if (err.status == 401 && err.error.error == 'unauthorized') {
                isShowTip && this.toastService.showToast(err.error.error_description);
                this.validateTip = ErrorTipCode.VALIDATE_FAIL;
            } else {
                isShowTip && this.events.publish('checkNet');
                this.validateTip = ErrorTipCode.NETWORK_ANOMALY;
            }
            return null;
        })
    }

    private async verifyToken() {
        return this.http.get("/party/v1/checkTick").subscribe(result => {
            return (Object.keys(result).length === 0);
        }, (err) => {
            return false;
        })
    }

    private getEncodedBody(body: { [prop: string]: string }): string {
        let segments = [];
        for (let key in body) {
            segments.push(key + "=" + body[key]);
        }
        return encodeURI(segments.join("&"));
    }

    private updateConfiguration(response: TokenResponse) {
        this.profile.hasSubordinate.value = response.hasSubordinate;
        this.profile.userName.value = response.userName;
        this.profile.roles.value = response.roles[0].authority;
        this.profile.nickName.value = response.nickName;
        this.profile.profileUrl.value = response.profileURL;
        this.profile.agentLevel.value = response.agentLevel;
        this.personal.accessToken.value = response.access_token;
        this.personal.refreshToken.value = response.refresh_token;
        this.events.publish('loginIn');
    }

    //获取当前用户姓名
    public getCurrentUser() {
        return this.profile.userName.value;
    }

    //手势登录成功后 重置手势密码登录次数为0
    public updateGraphicErrorTimes() {
        this.personal.graphicErrorTimes.value = '0';
    }

    public pauseLog() {
        localStorage.setItem('pauseTime', Date.now() + '');
    }

    public async resumeCheck() {
        let now = Date.now();
        let pauseTime = Number(localStorage.getItem('pauseTime'));
        if (now - pauseTime > this.resumeTimeout) {
            return await this.getLoginPage();
        }
        return null;
    }
}

class PageName {
    static contentTab = "TabsPage";
    static basicLogin = "LoginPage";
    static graphicLogin = "GraphicLoginPage";
    static fingerprintLogin = "FingerprintLoginPage";
}

class TokenResponse {
    access_token: string;
    refresh_token: string;
    hasSubordinate?: string;
    userName?: string;
    roles?: UserRole[];
    nickName?: string;
    profileURL?: string;
    agentLevel?: string;
}

class UserRole {
    authority: string;
}

/**
 * 用于错误提示信息
 */
enum ErrorTipCode {
    VALIDATE_FAIL = 0,//密码用户名验证失败
    NETWORK_ANOMALY = 1,//网络异常
}
